package com.congee02.dbservlet.framework.jdbc;

import lombok.extern.slf4j.Slf4j;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 数据库连接模板，该类包可见
 */
@Slf4j
class JdbcConnectionTemplate {

    private final String name;                  // 数据库连接池名称

    private final String driverClassName;       // 驱动全限定名
    private final String url;                   // 数据库 URL
    private final String username;              // 用户名
    private final String password;              // 密码


    /**
     * 核心连接池集合，由 lock 保证其线程安全
     */
    private final CopyOnWriteArrayList<Connection> coreConnectionPool;

    private final ReentrantLock lock = new ReentrantLock();    // 获取和释放连接，打开连接和关闭连接的锁

    private final int coreSize;
    private final int maxSize;
    private final AtomicInteger totalAliveSize = new AtomicInteger();
    private final AtomicBoolean shutdown = new AtomicBoolean(false);

    /**
     * 构造器包可见
     */
    JdbcConnectionTemplate(String name, String driverClassName, String url, String username, String password, String coreSize, String maxSize) {
        this.name = name;
        this.driverClassName = driverClassName;
        loadDriver();
        this.url = url;
        this.username = username;
        this.password = password;
        this.coreSize = Integer.parseInt(coreSize);
        this.maxSize = Integer.parseInt(maxSize);
        if (this.coreSize <= 0 || this.maxSize <= 0) {
            throw new IllegalArgumentException("The size must be greater than 0");
        }
        if (this.maxSize < this.coreSize) {
            throw new IllegalArgumentException("The maxSize must be greater than or equal to initialSize");
        }
        this.coreConnectionPool = new CopyOnWriteArrayList<>();
        initConnections(this.coreSize);
    }

    private void initConnections(int coreSize) {
        lock.lock();
        try {
            for (int i = 0 ; i < coreSize ; i ++ ) {
                coreConnectionPool.add(createConnection());
            }
            this.totalAliveSize.set(coreSize);
        } catch (SQLException e) {
            log.warn("SQL Exception: {}", e);
        } finally {
            lock.unlock();
        }
    }

    /**
     *
     * 加载 JDBC 驱动 (执行 static 代码块，注册驱动，让 JDBC 找到驱动类)
     *
     * static {
     *     try {
     *         java.sql.DriverManager.registerDriver(new Driver());
     *     } catch (SQLException E) {
     *         throw new RuntimeException("Can't register driver!");
     *     }
     * }
     */
    private void loadDriver() {
        try {
            Class.forName(driverClassName);
        } catch (ClassNotFoundException e) {
            log.warn("ClassNotFoundException: {}", e);
        }
    }

    /**
     * 获取数据连接对象
     */
    Connection getConnection() {
        Connection connection = null;
        lock.lock();
        try {
            if (! coreConnectionPool.isEmpty()) {
                connection = coreConnectionPool.remove(coreConnectionPool.size() - 1);
            }
            // 连接池的连接数量只可能 >= coreSize
            else if (totalAliveSize.get() < maxSize) {
                connection = createConnection();
                totalAliveSize.incrementAndGet();
            }
        } catch (SQLException e) {
            log.warn("SQL Exception: {}", e);
        } finally {
            lock.unlock();
        }
        return connection;
    }

    /**
     * 释放连接
     */
    void releaseConnection(Connection connection) {
        lock.lock();
        try {
            if (coreConnectionPool.size() < coreSize && ! connection.isClosed()) {
                coreConnectionPool.add(connection);
            } else if (coreConnectionPool.size() >= coreSize) {
                connection.close();
                totalAliveSize.decrementAndGet();
            } else {
                coreConnectionPool.add(createConnection());
            }
        } catch (SQLException e) {
            log.warn("SQL Exception: {}", e);
        } finally {
            lock.unlock();
        }
    }

    /**
     * 回收连接资源
     */
    void shutDown() {
        lock.lock();
        try {
            for (Connection connection : coreConnectionPool) {
                connection.close();
            }
            shutdown.set(true);
        } catch (SQLException e) {
            log.warn("SQL Exception: {}", e);
        } finally {
            lock.unlock();
        }
    }

    public boolean isShutdown() {
        return shutdown.get();
    }

    private synchronized Connection createConnection() throws SQLException {
        if (isShutdown()) {
            throw new IllegalStateException("The pool has been shut down.");
        }
        return DriverManager.getConnection(url, username, password);
    }

}
